package client;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Random;


import config.Config;

public class Client extends Thread {
	Socket socket;
	BufferedReader in;
	PrintWriter out;
	
	ArrayList<Measurement> result;
	int duration_minutes;
	int clientID;
	long client_start_time;
	long client_stop_time;
	long think_start_time;
	String middle_man_host;
	
	public Socket getSocket() {
		return socket;
	}
	
	public Client(int _duration_minutes, int _clientID, String _middle_man_host) {
		result = new ArrayList<Measurement>();
		duration_minutes = _duration_minutes;
		clientID = _clientID;
		middle_man_host = _middle_man_host;
		think_start_time = 0;
		connect();
	}
	
	public ArrayList<Measurement> getResult() {
		return result;
	}
	
	protected void runQuery(String queryName, String sql) {
		System.err.println("Client " + clientID + " - " + queryName + " => starting...");
		
		sql = sql.replaceAll("\\n", "NEWLINE");
		String fromServer;
		
		long think_stop_time = System.currentTimeMillis();
		long response_start_time = think_stop_time;
		
		if(think_start_time != 0) {
			// This is not the first query
			System.err.println("Client " + clientID + " - Thinking => " + (think_stop_time - think_start_time) + " ms");
			result.add(new ThinkMeasurement(clientID, think_start_time, think_stop_time));
		}
		
		try {
			// Send the query to the Middle Man
			out.println(sql);
			
			// Receive the result from the Middle Man
			fromServer = in.readLine();
			
			boolean success;
			
			ArrayList<String> rows = new ArrayList<String>();		
			if(fromServer != null && fromServer.startsWith("SUCCESS")) {
				success = true;
				String tmp = fromServer.substring(7);		
				int numberOfRows = Integer.parseInt(tmp);		
				for(int i=0; i<numberOfRows; i++) {		
					tmp = in.readLine();		
					rows.add(tmp);		
				}		
			} else {
				success = false;
			}
			
			long response_stop_time = System.currentTimeMillis();
			
			think_start_time = response_stop_time;
			
			// If the query execution was OK, we should have an Y as a result
			String line = "";
			if(success) {
				line = "Client " + clientID + " - " + queryName +  " => " + (response_stop_time - response_start_time) + " ms" + " => \n" + fromServer;
			} else {
				line = "Client " + clientID + " - " + queryName + " => " + (response_stop_time - response_start_time) + " ms" + " => FAIL";
			}
			
			// Print it out to stderr
			System.err.println(line);
			
			// Print the rows out to the live file
			LiveFileWriter.getInstance().SerialWrite(line, rows);
			
			// Record the result
			result.add(new ResponseMeasurement(clientID, response_start_time, response_stop_time, queryName, success));
			
			String[] lines = in.readLine().split(";");
			if (lines[0].equals ("PROCESSING")) {
				long processing_start_time, processing_stop_time;
				String host = lines [1];
				processing_start_time = Long.parseLong(lines [2]);
				processing_stop_time = Long.parseLong(lines [3]);
				
				result.add(new ProcessingMeasurement(clientID,
						processing_start_time, processing_stop_time, host, queryName,
						success));
			}
		} catch (IOException e) {
			System.err.println("Client " + clientID + " - exception: " + e.getMessage());
		}
	}
	
	public void run() {
		System.err.println("Client " + clientID + " running...");
		client_start_time = System.currentTimeMillis();
		int currentQuery = new Random().nextInt(22);
		long duration_milliseconds = ((long)duration_minutes) * 60 * 1000;
		while(true) {
			if(System.currentTimeMillis() - client_start_time > duration_milliseconds) {
				break;
			}
			runQuery("Q" + Config.queries_execution_order[currentQuery], QueryGenerator.Q(Config.queries_execution_order[currentQuery]));
			currentQuery ++;
			if(currentQuery > 21) {
				currentQuery = 0;
			}
		}
		client_stop_time = System.currentTimeMillis();
		double running_minutes = ((double)(client_stop_time - client_start_time)) / (1000 * 60);
		long actual_running_minutes = (long)running_minutes;
		System.err.println("Client " + clientID + " done, ran for " + actual_running_minutes + " minutes");
		disconnect();
	}
	
	protected void connect() { 
		socket = null;
		try {
			socket = new Socket(middle_man_host, Config.middle_man_port);
			in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			out = new PrintWriter(socket.getOutputStream(), true);
		} catch (UnknownHostException e) {
			System.err.println("Client " + clientID + " could not connect: Unknown host " + middle_man_host);
			e.printStackTrace();
		} catch (IOException e) {
			System.err.println("Client " + clientID + " could not connect: IO Error");
			e.printStackTrace();
		}
	}
	
	protected void disconnect() {
		try {
			out.close();
			in.close();
			socket.close();
		} catch (IOException e1) {
			System.err.println("Client " + clientID + " did not disconnect successfully.");
			e1.printStackTrace();
		}
	}
}
